#include <iostream>
#include <fstream>
#include <limits>
#include <list>

/* Knihovna podporující MPI rozhraní */
#include "mpi.h"
#include "Edge.h"
#include "Graph.h"
#include "SpanningTree.h"
#include "stdlib.h"


#define CHECK_MSG_AMOUNT  100
#define MSG_WORK_REQUEST 1000
#define MSG_WORK_SENT    1001
#define MSG_WORK_NOWORK  1002
#define MSG_TOKEN        1003
#define MSG_FINISH       1004
#define MSG_NEW_MINIMUM  1005

#define STATUS_WORKING  2001
#define STATUS_IDLE     2002
#define STATUS_FINISHED 2999

#define WHITE_PROCESS 3001
#define BLACK_PROCESS 3002

#define WHITE_TOKEN 3003
#define BLACK_TOKEN 3004


using namespace std;


Graph* LoadData(char* fileName);
void PrintGraph(Graph* g);
void PrintSpanningTree(SpanningTree* s);
bool sendWork(int target, MPI_Request* request, int* sendData, int maxSendDataLenght, list<Edge*> &stack, SpanningTree* tree);
void sendFirstEdge(int target, int* sendData, Edge* edge);
void receiveWork(MPI_Status* status, int* sendData, int maxSendDataLenght, list<Edge*> &stack, SpanningTree* tree);
void receiveFirstEdge(MPI_Status* status, int* sendData, list<Edge*> &stack);

void sendNewMin(int minDegree, int my_rank, int processorCount);

int main(int argc, char **argv) {
	// program musí mít jeden parametr - cesta k souboru se vstupem
    if (argc != 2) {
        cout << "Nesprávné parametry programu" << endl;;
        return 1;
    }

    /* ptsl jsem se na praci */
    bool wasRequestForWork = false;
    /*Posílaná data - mozno zmenit dle implementace na jiny typ podporovany MPI*/
    int message[100];
    /*Cislo meho procesu - 0 je master*/
    int my_rank;
    /*Pocet vsech procesu*/
    int processorCount;
    /*Slouzi pro pravidelne kontrolovani prijatych zprav*/
    unsigned int checkCounter;
    /*Koho se mam zeptat na praci*/
    unsigned int processToAskForWork;
    /*Aktualni stav procesu*/
    int processStatus = 0;
    /*Ukoncovaci token*/
    int token = 0;
    /*Naanaanananaaa*/
    int color = WHITE_PROCESS;
    /**/
    bool vectorSent = false;
    /**/
    bool isResult = false;
    bool wasWorking = false;

	/*mereni casu */
	double time1, time2 = 0;


    MPI_Status status;
    MPI_Request request;

    int flag;
    const int minBound = 2;
    int i;


	/*nacteni vstupu*/
	
	Graph* graph = LoadData(argv[1]);
	if(graph == NULL)
	{
		cout << "Nacteni dat selhalo!" << endl;
		return 2;
	}

    //sendData
    int maxSendDataLength = (graph->NodesCount * 2 + 1) * 2 + 2;
    int* sendData = new int[maxSendDataLength];

    //PrintGraph(graph);

    int startNode = 0;
    int minDegree = numeric_limits<int>::max();
    vector<Edge> minSpanningTree;

    SpanningTree* spanningTree = new SpanningTree(graph->NodesCount, startNode);
    list<Edge*> myStack;
    Edge* currentEdge;


    /* Inicializace MPI prostředí */
    MPI_Init(&argc, &argv);

    /*INICIALIZACE PROCESŮ - ZÁKLADNÍ ROZDĚLENÍ PRÁCE*/

    /* Zjistí jaký jsem proces a uloží do my_rank */
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    /*Zjistí kolik procesoru se ucastni vypoctu*/
    MPI_Comm_size(MPI_COMM_WORLD, &processorCount);


	/*cekani na vsechny procesy pred roydelenim dat*/
	MPI_Barrier(MPI_COMM_WORLD);
	cout << "MPI_Barrier START" << endl;

	if(my_rank == 0) {
		time1 = MPI_Wtime();
	}

    /*Nactu prvni stupen stavoveho prostoru. Vsechny az na jeden poslu ostatnim procesorum, pro ty, pro ktere
     nemam praci poslu NOWORK flag*/
	
	
	
	if (my_rank == 0) {
		//nove rozdelovani
		int startAdjacencySize = graph->AdjacencyList[startNode].size();
		if(startAdjacencySize < 1){
			cout << "Nesouvisly Graf, startNode ma 0 sousedu!!" << endl;
			return 3;
		}

		cout << "zacinam rozesilat" << endl;

		int lastSentAdjacent = 1;

		for(i = 1; i < processorCount; i++) 
		{
			if(i < startAdjacencySize)
			{
				int adjacent = graph->AdjacencyList[startNode][i];
				Edge* e = new Edge(startNode, adjacent, false);
				sendFirstEdge(i, sendData, e);
				lastSentAdjacent++;
				cout << "Poslal jsem praci procesu " << i << endl;
			}
			else
			{
				MPI_Send(0, 0, MPI_INT, i, MSG_WORK_NOWORK, MPI_COMM_WORLD);
				cout << "Poslal jsem NO_WORK procesu " << i << endl;
			}
		}

		int adjacentForMe = graph->AdjacencyList[startNode][0];
		myStack.push_back(new Edge(startNode, adjacentForMe, false));
		cout << "Pridelil jsem si prvniho souseda" << endl;
		//prideleni nerozeslane prace masterovi
		while(lastSentAdjacent < startAdjacencySize){
			adjacentForMe = graph->AdjacencyList[startNode][i];
			myStack.push_back(new Edge(startNode, adjacentForMe, false));
			cout << "Pridelil jsem si " << lastSentAdjacent << " souseda" << endl;
			lastSentAdjacent++;
		}

		processStatus = STATUS_WORKING;

	} else {
		/* Jsem slave proces. Cekam na zpravu obsahujici praci. Pokud dostanu informaci, ze pro me
			prace nevysla, nastavim svuj status na "bez prace". */
		while (processStatus == 0) {
			MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &status);
			if (flag) {

				switch (status.MPI_TAG) {
						/*Prisla zadost o praci, jsem IDLE, posilam tedy NO_WORK*/
					case MSG_WORK_NOWORK:
						MPI_Recv(0, 0, MPI_INT, 0, MSG_WORK_NOWORK, MPI_COMM_WORLD, &status);
						processStatus = STATUS_IDLE;
						break;
					case MSG_WORK_SENT:
						receiveFirstEdge(&status, sendData, myStack);
						processStatus = STATUS_WORKING;
						break;
				}
			}
		}

	}
	
    /*Nastaveni koho se ptat na praci - aby nedoslo zbytecne k hromadnemu ptani se procesu 0*/
    processToAskForWork = (my_rank + 1) % processorCount;

    MPI_Barrier(MPI_COMM_WORLD);
	cout << "MPI_Barrier INIT WORK SENT" << endl;
    /*KONEC INICIALIZACE*/



    /*Hlavni beh procesu - do nej je treba presunout expand() obsahujici while cyklus s !myStack.empty()
     toci se vlastne porad dokola budto pracuju nebo praci nemam a pak o ni postupne zadam jednotlive
     procesy*/
    while (processStatus != STATUS_FINISHED) {

        if ((my_rank == 0) && (processorCount == 1) && (processStatus == STATUS_IDLE)) {
            processStatus = STATUS_FINISHED;
        }
        /*CHOVANI PROCESU POKUD NEMÁ ŽÁDNOU PRÁCI*/
        if (processStatus == STATUS_IDLE) {
            
            /*ADUV bod 1*/
            if ((my_rank == 0) && (!vectorSent)) {
                color = WHITE_PROCESS;
                token = WHITE_TOKEN;
                MPI_Send(&token, 1, MPI_INT, 1, MSG_TOKEN, MPI_COMM_WORLD);
                vectorSent = true;
				//cout << "Procces 0 vyslal token" << endl;

            } else if ((token != 0) && (my_rank != 0)) {
                //cout << "[MPI_Send] " << "Sender: " << my_rank << " Target " << (my_rank + 1) % processorCount << " Message: " << MSG_TOKEN << endl;
				int nextProcesRank = (my_rank + 1) % processorCount;
				//cout << "Proces " << my_rank << "poslal token dal do " << nextProcesRank << endl;
                MPI_Send(&token, 1, MPI_INT, nextProcesRank, MSG_TOKEN, MPI_COMM_WORLD);
				color = WHITE_PROCESS;
                token = 0;

            }
            
            int target = processToAskForWork % processorCount;
            if (!wasRequestForWork) {
                if (target != my_rank) {
					//cout << "Zadost o praci: " << my_rank << " zada proces " << target << " o praci" << endl;
                    MPI_Send(0, 0, MPI_INT, target, MSG_WORK_REQUEST, MPI_COMM_WORLD);
                    wasRequestForWork = true;
                    
                }
            }

            processToAskForWork++;


            MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &status);

            if (flag) {

                switch (status.MPI_TAG) {
                        /*Prisla zadost o praci, jsem IDLE, posilam tedy NO_WORK*/
                    case MSG_WORK_REQUEST:
                        MPI_Recv(0, 0, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
                        MPI_Send(0, 0, MPI_INT, status.MPI_SOURCE, MSG_WORK_NOWORK, MPI_COMM_WORLD);
                        //cout << "[MPI_Recv] " << "Sender: " << status.MPI_SOURCE << " Target " << my_rank << " Message: " << status.MPI_TAG << endl;
                        break;
                        /*Prisla prace - prijmam praci, naplnuji currentEdge a spanningTree. Nastavuji STATUS_WORKING*/
                    case MSG_WORK_SENT:
                        receiveWork(&status, sendData, maxSendDataLength, myStack, spanningTree);
                        wasRequestForWork = false;
                        processStatus = STATUS_WORKING;
                        //cout << "[MPI_WORK_RECEIVED] Process:" << my_rank << endl;
                        break;
                        /*Zamitava odpoved na zadost o práci*/
                    case MSG_WORK_NOWORK:
                        MPI_Recv(0, 0, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
                        wasRequestForWork = false;
                        processStatus = STATUS_IDLE;
                        //cout << "[MPI_Recv NO?WORK] " << my_rank << endl;
                        break;
                        /*Prisel TOKEN zajistujici ukonceni vypoctu ADUV, resim podle toho zda jsem MASTER a jakou mam barvu*/
                    case MSG_TOKEN:
                        //cout << "[MPI_Recv] " << "Sender: " << status.MPI_SOURCE << " Target " << my_rank << " Message: " << status.MPI_TAG << endl;
                        MPI_Recv(&token, 1, MPI_INT, status.MPI_SOURCE, MSG_TOKEN, MPI_COMM_WORLD, &status);

                        if ((my_rank == 0) && (token == BLACK_TOKEN)) {
                            cout << "MASTER PRIJAL BLACK TOKEN" << endl;
                            vectorSent = false;


                        }

                        if ((my_rank == 0) && (token == WHITE_TOKEN)) {
                            processStatus = STATUS_FINISHED;
                            cout << "MAIN PROCESS STATUS_FINISHED" << endl;

                        } else {
                            if ((color == BLACK_PROCESS)) {
                                token = BLACK_TOKEN;
                            }
                        }

                        break;
                    case MSG_NEW_MINIMUM:
                        int message;
                        MPI_Recv(&message, 1, MPI_INT, MPI_ANY_SOURCE, MSG_NEW_MINIMUM, MPI_COMM_WORLD, &status);
                        if(message == minBound){
                            isResult = false;
							cout << "Prijat minBound procesem " << my_rank << endl;
                            processStatus = STATUS_FINISHED;
                        }
                        else if (message < minDegree) {
                            minDegree = message;
                            isResult = false;
                        }
                        break;
                        /*Prisla ukoncujici zprava od MASTER. Nastavuji STAV na STATUS_FINISHED*/

                    case MSG_FINISH:
                        MPI_Recv(0, 0, MPI_INT, MPI_ANY_SOURCE, MSG_FINISH, MPI_COMM_WORLD, &status);
                        processStatus = STATUS_FINISHED;
                        //cout << "[MPI_Recv] " << "Sender: " << status.MPI_SOURCE << " Target " << my_rank << " Message: " << status.MPI_TAG << endl;
                        break;
                    default:; //chyba("neznamy typ zpravy");
                        break;
                }
            }
        }

        /*KONEC CHOVÁNÍ PROCESU BEZ PRÁCE*/

        /*CHOVÁNÍ PROCESU POKUD MÁ PRÁCI*/
        if (processStatus == STATUS_WORKING) {


            while (!myStack.empty() && processStatus != STATUS_FINISHED) {
                wasWorking = true;
                //cout << "[WORKING] Process: " << my_rank << endl;
				if(processorCount > 1){
					/*Jednou za 100 cyklu - budeme testovat kolik je optimalni se podivam jestli mi nekdo neposila zpravu
					 pokud ano, zjistim o jakou zpravu se jedna a obslouzim ji, pak opet expanduji - expanze je potreba vyndat
					 z main...*/
					checkCounter++;
					if ((checkCounter % CHECK_MSG_AMOUNT) == 0) {
						MPI_Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &flag, &status);
						if (flag) {
							//prisla zprava, je treba ji obslouzit
							//v promenne status je tag (status.MPI_TAG), cislo odesilatele (status.MPI_SOURCE)
							//a pripadne cislo chyby (status.MPI_ERROR)
							bool workSended;
							switch (status.MPI_TAG) {
								case MSG_WORK_REQUEST:
									MPI_Recv(0, 0, MPI_INT, MPI_ANY_SOURCE, MSG_WORK_REQUEST, MPI_COMM_WORLD, &status);
									//cout << "[MPI_WORK_REQUEST] Proces" << status.MPI_SOURCE << " zada o praci proces: " << my_rank << endl;
									workSended = sendWork(status.MPI_SOURCE, &request, sendData, maxSendDataLength, myStack, spanningTree);

									if ((workSended) && (my_rank > status.MPI_SOURCE)) {
										color = BLACK_PROCESS;
										if (token != 0) {
											token = BLACK_TOKEN;
										}
									}

									break;
								case MSG_NEW_MINIMUM:
									int message;
									MPI_Recv(&message, 1, MPI_INT, MPI_ANY_SOURCE, MSG_NEW_MINIMUM, MPI_COMM_WORLD, &status);
									if(message == minBound){
										isResult = false;
										cout << "Prijat minBound procesem " << my_rank << endl;
										processStatus = STATUS_FINISHED;
									}
									else if (message < minDegree) {
										minDegree = message;
										isResult = false;
									}
									break;
								case MSG_TOKEN:
								//cout << "WORKING a Přijat TOKEN Process:" << my_rank << endl;
									MPI_Recv(&token, 1, MPI_INT, MPI_ANY_SOURCE, MSG_TOKEN, MPI_COMM_WORLD, &status);
									if (my_rank == 0) {
										//cout << "[MPI_TOKEN_RECIVED] My process:    " << my_rank << endl;
										vectorSent = false;
									}
									break;

								default:; //chyba("neznamy typ zpravy");
									break;
							}
						}
					}
				}

                currentEdge = myStack.back();
                if (!currentEdge->IsVisited) {
                    currentEdge->IsVisited = true;
                    spanningTree->AddEdge(currentEdge);

                    int spanningTreeDegree = spanningTree->GetDegree();
                    if (spanningTreeDegree >= minDegree) {
                        continue;
                    }

                    if (spanningTree->IsSpanningTree()) {
                        //PrintSpanningTree(spanningTree);
                        if (spanningTreeDegree < minDegree) {
                            minDegree = spanningTreeDegree;


                            sendNewMin(minDegree, my_rank, processorCount);
							/*cout << "New minimum " << minDegree  << " odeslo z " << my_rank << endl;*/
                            isResult = true;

                            minSpanningTree.clear();
                            int i;
                            for (i = 0; i < spanningTree->Edges.size(); i++) {
                                minSpanningTree.push_back(Edge(spanningTree->Edges[i]->FromNode, spanningTree->Edges[i]->ToNode, false));
                            }
                        }
                        if (spanningTreeDegree == minBound) {
                            processStatus = STATUS_FINISHED;
                            break;
                        }
                    }

                    //projde vsechny sousedy (v zadanem grafu) vsech uzlu co jsou v kostre a prida
                    //na zasobnik hrany vedouci z uzlu v kostre do souseda tohoto uzlu v zadanem grafu
                    //ktery jeste v kostre neni
                    set<int>::iterator it;
                    int i;
                    for (it = spanningTree->Nodes.begin(); it != spanningTree->Nodes.end(); ++it) {

                        int treeNode = *it;
                        for (i = 0; i < graph->AdjacencyList[treeNode].size(); i++) {
                            int adjacent = graph->AdjacencyList[treeNode][i];
                            if (spanningTree->Nodes.find(adjacent) == spanningTree->Nodes.end()) {
                                myStack.push_back(new Edge(treeNode, adjacent, false));
                            }
                        }
                    }

                } else {
                    myStack.pop_back();
                    spanningTree->RemoveEdge(currentEdge);
                    delete currentEdge;
                }
            }
            
            if(processStatus != STATUS_FINISHED) {
                processStatus = STATUS_IDLE;
            }
        }
    }

	//cout << "Skoncil jsem: " << my_rank << endl;
    //deleting
    delete graph;
    delete spanningTree;
    delete[] sendData;

	
    if (my_rank == 0 && minDegree != minBound) {
        for (i = 1; i < processorCount; i++) {
            MPI_Send(0, 0, MPI_INT, i, MSG_FINISH, MPI_COMM_WORLD);
            cout << "[MPI_Send] " << "Sender: " << my_rank << " Target " << i << " Message: " << MSG_FINISH << endl;
        }
    }


	MPI_Barrier(MPI_COMM_WORLD);
	cout << "MPI_Barrier END" << endl;

	

	if(my_rank == 0) {
		time2 = MPI_Wtime();

		double totalTime = time2 - time1;

		cout << "Celkovy cas vypoctu: " << totalTime << endl;
		printf ("Spotrebovany cas je %f.\n", totalTime);
	}


    if (isResult) {
        cout << "Minimum degree: " << minDegree << endl;
        cout << "Spanning Tree:" << endl;
        for (i = 0; i < minSpanningTree.size(); i++) {
            cout << minSpanningTree[i].FromNode << "->" << minSpanningTree[i].ToNode << endl;
        }
    }

    if (wasWorking){
        cout << "PROCESOR: " <<my_rank<<" pracoval." <<endl;
    }

    //char a;
    //cin >> a;




    MPI_Finalize();

    return (0);

}

void sendNewMin(int minDegree, int resultRank, int processorCount) {
    for (int i = 0; i < processorCount; i++) {
        if (i != resultRank) {
			//cout << "Odesilam New minimum " << minDegree << " do " << resultRank << endl;
            MPI_Send(&minDegree, 1, MPI_INT, i, MSG_NEW_MINIMUM, MPI_COMM_WORLD);
			
        }
    }
}

/*Posle praci procesoru target, do zpravi zakoduje hranu, kterou posila a stavajici strom*/
bool sendWork(int target, MPI_Request* request, int* sendData, int maxSendDataLenght, list<Edge*> &stack, SpanningTree* tree) {
    list<Edge*>::reverse_iterator it;

    int edgesToSendCount = 0;
    //      cout << "--------------sendWork stack:" << endl;

    it = stack.rbegin();
    while (it != stack.rend()) {
        if ((*it)->IsVisited) {
            break;
        }
        edgesToSendCount++;
        it++;
    }

    if (edgesToSendCount < 2) {
        //              cout << "No work to send" << endl;
        MPI_Send(0, 0, MPI_INT, target, MSG_WORK_NOWORK, MPI_COMM_WORLD);
        //cout << "Sending NO_WORK" << endl;
        return false;
    }

    //posli jen polovinu s toho co mam v aktualni vysce stavoveho prostoru
    edgesToSendCount = edgesToSendCount / 2;

    int dataLenght = (edgesToSendCount * 2) + 2 + (tree->Edges.size() * 2);
    //to co budu posilat
    // format dat: (0, 1)*pocet hran co poslu , -1(oddelovac), (0,1)*pocet hran v kostre, -1


    //      cout << dataLenght << endl;

    it = stack.rbegin();

    int iSend = 0;
    for (int i = 0; i < edgesToSendCount; i++) {
        Edge* e = *it;

        ////pridat hranu do toho co budu posilat
        ////odebrat ji ze stacku tohohle procesu
        ////a smazat ji!!
        sendData[iSend] = e->FromNode;
        sendData[iSend + 1] = e->ToNode;
        stack.remove(e);
        delete e;

        //cout << e->FromNode << "->" << e->ToNode << " ; " << e->IsVisited << endl;
        iSend += 2;
        it++;
    }
    sendData[iSend] = -1;
    iSend++;
    int treeSize = tree->Edges.size();
    for (int i = 0; i < treeSize; i++) {
        sendData[iSend] = tree->Edges[i]->FromNode;
        sendData[iSend + 1] = tree->Edges[i]->ToNode;
        iSend += 2;
    }
    sendData[iSend] = -1;

    //odeslani dat targetovy
    MPI_Send(sendData, dataLenght, MPI_INT, target, MSG_WORK_SENT, MPI_COMM_WORLD);
    //      for(int i=0; i < dataLenght; i++)
    //      {
    //              cout << sendData[i] << ", ";
    //      }
    //      cout<< endl;

    //cout << "--------------sendWork stack END" << endl;
    return true;
}

void receiveWork(MPI_Status* status, int* sendData, int maxSendDataLenght, list<Edge*> &stack, SpanningTree* tree) {
    //      cout << "--------------receiveWork stack:" << endl;
    tree->Reset();

    //MPI Receive
    MPI_Recv(sendData, maxSendDataLenght, MPI_INT, MPI_ANY_SOURCE, MSG_WORK_SENT, MPI_COMM_WORLD, status);
    //MPI get_Count
    int dataLenght = maxSendDataLenght;
    MPI_Get_count(status, MPI_INT, &dataLenght);

    bool wasMinusOne = false;
    for (int i = 2; i < dataLenght; i += 2) {
        if (wasMinusOne) {
            //                      cout << "Spanning tree: " << sendData[i-2] << "->" << sendData[i-1] << endl;
            //plnim kostru
            tree->AddEdge(new Edge(sendData[i - 2], sendData[i - 1], false));
        } else {
            //                      cout << "Zasobnik: " << sendData[i-2] << "->" << sendData[i-1] << endl;
            //plnim zasobnik
            stack.push_back(new Edge(sendData[i - 2], sendData[i - 1], false));
        }

        if (sendData[i] == -1) {
            if (i == dataLenght - 1) {
                break;
            }
            i++;
            if (sendData[i] == -1) {
                break;
            }
            wasMinusOne = true;
        }
    }

    //      cout << "--------------receiveWork stack END" << endl;
}

void sendFirstEdge(int target, int* sendData, Edge* edge) {
    sendData[0] = edge->FromNode;
    sendData[1] = edge->ToNode;
    MPI_Send(sendData, 2, MPI_INT, target, MSG_WORK_SENT, MPI_COMM_WORLD);
    delete edge;
}

void receiveFirstEdge(MPI_Status* status, int* sendData, list<Edge*> &stack) {
    MPI_Recv(sendData, 2, MPI_INT, MPI_ANY_SOURCE, MSG_WORK_SENT, MPI_COMM_WORLD, status);
    stack.push_back(new Edge(sendData[0], sendData[1], false));
}

void PrintGraph(Graph* g) {
    cout << "Nodes Count: " << g->NodesCount << endl;
    int i, j;
    for (i = 0; i < g->AdjacencyList.size(); i++) {
        for (j = 0; j < g->AdjacencyList[i].size(); j++) {
            cout << g->AdjacencyList[i][j];
        }
        cout << endl;
    }
}

void PrintSpanningTree(SpanningTree* s) {
    cout << "Edges in spanning tree:" << endl;
    int i;
    for (i = 0; i < s->Edges.size(); i++) {
        cout << s->Edges[i]->FromNode << "->" << s->Edges[i]->ToNode << endl;
    }
}


Graph* LoadData(char* fileName) {
	ifstream infile;
	infile.open(fileName);

	if(!infile) { 
		cout << "Cannot open file.\n"; 
		return NULL; 
	} 

	Graph* g = new Graph(infile); 
	
	infile.close();

	return g;
}